import { EventEmitter } from 'node:events'
import { WebSocket } from 'ws'
import { ApiMsgEnum, binaryDecode, binaryEncode, IModel } from '../Common'
import { buffer2ArrayBuffer } from '../Utils'
import { MyServer } from './MyServer'

interface IItem {
  cb: Function
  ctx: unknown
}

export class Connection extends EventEmitter {
  msgMap: Map<ApiMsgEnum, Array<IItem>> = new Map()

  constructor(
    private server: MyServer,
    private ws: WebSocket
  ) {
    super()
    this.ws.on('close', () => {
      this.emit('close')
    })

    this.ws.on('message', (buffer: Buffer) => {
      try {
        const json = binaryDecode(buffer2ArrayBuffer(buffer))
        const { name, data } = json

        // 如果是api，则调用api
        if (this.server.apiMap.has(name)) {
          try {
            const cb = this.server.apiMap.get(name)
            const res = cb.call(null, this, data)
            this.sendMsg(name, {
              success: true,
              res
            })
          } catch (error) {
            this.sendMsg(name, {
              success: false,
              error: error.message
            })
          }
        } else {
          try {
            // 如果是消息，则调用消息
            if (this.msgMap.has(name)) {
              this.msgMap.get(name).forEach(({ cb, ctx }) => {
                cb.call(ctx, this, data)
              })
            }
          } catch (error) {
            console.log(error)
          }
        }
      } catch (error) {
        console.log(error)
      }
    })
  }

  sendMsg<T extends keyof IModel['msg']>(name: T, data: IModel['msg'][T]) {
    // 将msg转为Buffer
    const da = binaryEncode(name, data)
    const buffer = Buffer.from(da.buffer)
    this.ws.send(buffer)
  }

  listenMsg<T extends keyof IModel['msg']>(
    name: T,
    cb: (connection: Connection, args: IModel['msg'][T]) => void,
    ctx: unknown
  ) {
    if (this.msgMap.has(name)) {
      this.msgMap.get(name).push({ cb, ctx })
    } else {
      this.msgMap.set(name, [{ cb, ctx }])
    }
  }

  unListenMsg<T extends keyof IModel['msg']>(
    name: T,
    cb: (connection: Connection, args: IModel['msg'][T]) => void,
    ctx: unknown
  ) {
    if (this.msgMap.has(name)) {
      const index = this.msgMap.get(name).findIndex(i => cb === i.cb && i.ctx === ctx)
      if (index > -1) {
        this.msgMap.get(name).splice(index, 1)
      }
    }
  }
}
